package com.example.backend.service;

import com.example.backend.entity.StorageIn;
import com.example.backend.entity.StorageOut;
import com.example.backend.entity.Warehouse;
import com.example.backend.entity.StockRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.ArrayList;
import java.math.BigDecimal;

@Slf4j
@Service
public class WarehouseAllocationService {
    
    @Autowired
    private WarehouseService warehouseService;
    
    @Autowired
    private StockRecordService stockRecordService;
    
    /**
     * 入库时分配仓库 - 空闲让进策略
     * 在指定港口内，按仓库空闲程度分配，支持一次入库分到多个仓库
     * 
     * @param companyId 公司ID
     * @param portId 港口ID
     * @param totalWeight 总重量
     * @return 分配结果列表，包含仓库ID和分配重量
     */
    public List<AllocationResult> allocateWarehousesForStorageIn(Long companyId, Long portId, Double totalWeight) {
        log.info("开始为港口{}分配仓库，公司ID: {}, 总重量: {}", portId, companyId, totalWeight);
        
        // 获取该港口下所有有剩余容量的仓库，按当前容量排序（空闲让进）
        List<Warehouse> availableWarehouses = warehouseService.getAvailableWarehousesByPort(portId);
        
        log.info("港口{}下找到{}个可用仓库", portId, availableWarehouses.size());
        
        if (availableWarehouses.isEmpty()) {
            // 获取该港口下所有仓库（包括已满的）用于调试
            List<Warehouse> allWarehouses = warehouseService.getAllWarehouses().stream()
                .filter(w -> w.getPortId().equals(portId))
                .toList();
            
            if (allWarehouses.isEmpty()) {
                throw new RuntimeException(String.format("港口ID %d 下没有任何仓库，请先添加仓库", portId));
            } else {
                StringBuilder errorMsg = new StringBuilder();
                errorMsg.append(String.format("港口ID %d 下没有可用仓库。现有仓库情况：\n", portId));
                for (Warehouse w : allWarehouses) {
                    errorMsg.append(String.format("  仓库ID: %d, 名称: %s, 最大容量: %.2f, 当前容量: %.2f\n", 
                        w.getId(), w.getName(), w.getMaxCapacity(), w.getCurrentCapacity()));
                }
                errorMsg.append("所有仓库都已满或容量配置有误，请检查仓库容量设置");
                throw new RuntimeException(errorMsg.toString());
            }
        }
        
        // 计算总可用容量
        double totalAvailableCapacity = availableWarehouses.stream()
            .mapToDouble(warehouse -> warehouse.getMaxCapacity() - warehouse.getCurrentCapacity())
            .sum();
        
        log.info("港口{}总可用容量: {}", portId, totalAvailableCapacity);
        
        // 检查总容量是否足够
        if (totalAvailableCapacity < totalWeight) {
            throw new RuntimeException(String.format(
                "该港口装不下这么多货物！请求入库 %.2f 吨，但港口总剩余容量只有 %.2f 吨，还缺少 %.2f 吨容量",
                totalWeight, totalAvailableCapacity, totalWeight - totalAvailableCapacity
            ));
        }
        
        List<AllocationResult> results = new ArrayList<>();
        double remainingWeight = totalWeight;
        
        // 按当前容量排序，优先选择容量最小的仓库（空闲让进）
        for (Warehouse warehouse : availableWarehouses) {
            if (remainingWeight <= 0) break;
            
            double availableCapacity = warehouse.getMaxCapacity() - warehouse.getCurrentCapacity();
            if (availableCapacity <= 0) continue;
            
            // 计算本次分配的重量
            double allocatedWeight = Math.min(remainingWeight, availableCapacity);
            
            results.add(new AllocationResult(warehouse.getId(), warehouse.getPortId(), allocatedWeight));
            remainingWeight -= allocatedWeight;
            
            log.info("分配仓库ID: {}, 分配重量: {}, 剩余重量: {}", 
                warehouse.getId(), allocatedWeight, remainingWeight);
        }
        
        // 这个检查理论上不会触发，因为前面已经检查了总容量
        if (remainingWeight > 0) {
            throw new RuntimeException(String.format(
                "仓库分配失败！还剩余 %.2f 吨无法分配，请检查仓库容量配置",
                remainingWeight
            ));
        }
        
        log.info("仓库分配完成，共分配{}个仓库", results.size());
        return results;
    }
    
    /**
     * 出库时检查FIFO - 先进先出策略
     * 获取该公司在该港口下最早入库的库存记录（只返回有剩余库存的记录）
     */
    public List<StockRecord> getFIFOStockRecords(Long companyId, Long portId, Double requiredWeight) {
        // 获取该公司在该港口下所有有库存的记录，按时间排序
        List<StockRecord> availableStockRecords = stockRecordService.getAvailableStockRecordsByCompanyAndPort(companyId, portId);
        
        if (availableStockRecords.isEmpty()) {
            throw new RuntimeException("该公司在该港口下没有存货");
        }
        
        return availableStockRecords;
    }
    
    /**
     * 计算某条入库记录的剩余重量
     */
    public Double getRemainingWeight(Long inId) {
        return stockRecordService.getRemainingWeightByInId(inId).doubleValue();
    }
    
    /**
     * 检查公司是否在仓库有存货
     */
    public boolean hasCompanyStockInWarehouse(Long companyId, Long warehouseId) {
        List<StockRecord> stockRecords = stockRecordService.getStockRecordsByWarehouseId(warehouseId);
        return stockRecords.stream()
            .anyMatch(record -> record.getCompanyId().equals(companyId) && 
                               record.getRemainingWeight().compareTo(BigDecimal.ZERO) > 0 &&
                               record.getStatus() == 1);
    }
    
    /**
     * 更新仓库容量
     */
    public void updateWarehouseCapacity(Long warehouseId, Double weightChange) {
        Warehouse warehouse = warehouseService.getWarehouseById(warehouseId);
        if (warehouse != null) {
            warehouse.setCurrentCapacity(warehouse.getCurrentCapacity() + weightChange);
            warehouseService.updateWarehouse(warehouse);
        }
    }
    
    /**
     * 分配结果类
     */
    public static class AllocationResult {
        private Long warehouseId;
        private Long portId;
        private Double allocatedWeight;
        
        public AllocationResult(Long warehouseId, Long portId, Double allocatedWeight) {
            this.warehouseId = warehouseId;
            this.portId = portId;
            this.allocatedWeight = allocatedWeight;
        }
        
        public Long getWarehouseId() { return warehouseId; }
        public Long getPortId() { return portId; }
        public Double getAllocatedWeight() { return allocatedWeight; }
    }
} 